/**
 * @file
 * @brief Public QF-nano interface.
 * @ingroup qfn
 * @cond
 ******************************************************************************
 * Last updated for version 6.8.2
 * Last updated on  2020-03-08
 *
 *                    Q u a n t u m  L e a P s
 *                    ------------------------
 *                    Modern Embedded Software
 *
 * Copyright (C) 2005-2020 Quantum Leaps, LLC. All rights reserved.
 *
 * This program is open source software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alternatively, this program may be distributed and modified under the
 * terms of Quantum Leaps commercial licenses, which expressly supersede
 * the GNU General Public License and are specifically designed for
 * licensees interested in retaining the proprietary status of their code.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <www.gnu.org/licenses>.
 *
 * Contact information:
 * <www.state-machine.com/licensing>
 * <info@state-machine.com>
 ******************************************************************************
 * @endcond
 */
#ifndef QFN_H
#define QFN_H

/**
 * @description
 * This header file must be included in all modules that use QP-nano.
 * Typically, this header file is included indirectly through the
 * header file qpn_port.h.
 */

/****************************************************************************/
#ifndef QF_TIMEEVT_CTR_SIZE
/*! macro to override the default QTimeEvtCtr size.
 * Valid values 0U, 1U, 2U, or 4U; default 0U
 */
#define QF_TIMEEVT_CTR_SIZE 0U
#endif
#if (QF_TIMEEVT_CTR_SIZE == 0U)
/* no time events */
#elif (QF_TIMEEVT_CTR_SIZE == 1U)
typedef uint8_t QTimeEvtCtr;
#elif (QF_TIMEEVT_CTR_SIZE == 2U)
/*! type of the Time Event counter, which determines the dynamic
 * range of the time delays measured in clock ticks.
 */
/**
 * @description
 * This typedef is configurable via the preprocessor switch
 * #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are
 * as follows: @n
 * none when (QF_TIMEEVT_CTR_SIZE not defined or == 0U), @n
 * uint8_t  when (QF_TIMEEVT_CTR_SIZE == 1U); @n
 * uint16_t when (QF_TIMEEVT_CTR_SIZE == 2U); and @n
 * uint32_t when (QF_TIMEEVT_CTR_SIZE == 4U).
 */
typedef uint16_t QTimeEvtCtr;
#elif (QF_TIMEEVT_CTR_SIZE == 4U)
typedef uint32_t QTimeEvtCtr;
#else
#error "QF_TIMER_SIZE defined incorrectly, expected 1U, 2U, or 4U"
#endif

#if (QF_TIMEEVT_CTR_SIZE != 0U)
/*! Timer structure the active objects */
typedef struct {
    QTimeEvtCtr nTicks; /*!< timer tick counter */
#ifdef QF_TIMEEVT_PERIODIC
    QTimeEvtCtr interval; /*!< timer interval */
#endif                    /* QF_TIMEEVT_PERIODIC */
} QTimer;
#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */

#ifndef QF_MAX_TICK_RATE
/*! Default value of the macro configurable value in qpn_port.h */
#define QF_MAX_TICK_RATE 1U
#elif (QF_MAX_TICK_RATE > 4U)
#error "QF_MAX_TICK_RATE exceeds the 4U limit"
#endif

/****************************************************************************/
/*! QActive active object (based on QHsm-implementation)
 * @extends QHsm
 */
/**
 * @description
 * QActive is the base structure for derivation of active objects. Active
 * objects in QF-nano are encapsulated tasks (each embedding a state machine
 * and an event queue) that communicate with one another asynchronously by
 * sending and receiving events. Within an active object, events are
 * processed sequentially in a run-to-completion (RTC) fashion, while QF
 * encapsulates all the details of thread-safe event exchange and queuing.
 *
 * @note ::QActive is not intended to be instantiated directly, but rather
 * serves as the base structure for derivation of active objects in the
 * application code.
 *
 * @usage
 * The following example illustrates how to derive an active object from
 * ::QActive. Please note that the ::QActive member super_ is defined as
 * the __first__ member of the derived struct.
 * @include qfn_qactive.c
 */
typedef struct QActive {
    QHsm super; /**< derives from the ::QHsm base class */

#if (QF_TIMEEVT_CTR_SIZE != 0U)
    /*! Timer for the active object */
    QTimer tickCtr[QF_MAX_TICK_RATE];
#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */

    /*! priority of the active object (1..8) */
    uint8_t prio;

    /*! offset to where next event will be inserted into the buffer */
    uint8_t volatile head;

    /*! offset of where next event will be extracted from the buffer */
    uint8_t volatile tail;

    /*! number of events currently present in the queue
     * (events in the ring buffer + 1 event in the state machine)
     */
    uint8_t volatile nUsed;

} QActive;

/*! Virtual table for the QActive class
 * @extends QHsmVtable
 */
typedef struct {
    QHsmVtable super; /*!< inherits QHsmVtable */

#if (Q_PARAM_SIZE != 0U)
    /*! virtual function to asynchronously post (FIFO) an event to an AO
     * (task context).
     */
    /** @sa QACTIVE_POST() and QACTIVE_POST_X() */
    bool (*post)(QActive *const me, uint_fast8_t const margin, enum_t const sig, QParam const par);

    /*! virtual function to asynchronously post (FIFO) an event to an AO
     * (ISR context).
     */
    /** @sa QACTIVE_POST_ISR() and QACTIVE_POST_X_ISR() */
    bool (*postISR)(QActive *const me, uint_fast8_t const margin, enum_t const sig,
                    QParam const par);
#else
    bool (*post)(QActive *const me, uint_fast8_t const margin, enum_t const sig);
    bool (*postISR)(QActive *const me, uint_fast8_t const margin, enum_t const sig);
#endif
} QActiveVtable;

/*! protected "constructor" of an QActive active object. */
void QActive_ctor(QActive *const me, QStateHandler initial);

/*! special value of margin that causes asserting failure in case
 * event posting fails.
 */
#define QF_NO_MARGIN ((uint_fast8_t)0xFF)

#if (Q_PARAM_SIZE != 0U)
/*! Polymorphically posts an event to an active object (FIFO)
 * with delivery guarantee (task context).
 */
/**
 * @description
 * This macro asserts if the queue overflows and cannot accept the event.
 *
 * @param[in,out] me_   pointer (see @ref oop)
 * @param[in]     sig_  signal of the event to post
 * @param[in]     par_  parameter of the event to post.
 *
 * @sa QACTIVE_POST_X(), QActive_postX_(),
 * QACTIVE_POST_ISR(), QActive_postXISR_().
 *
 * @usage
 * @include qfn_post.c
 */
#define QACTIVE_POST(me_, sig_, par_)                               \
    do {                                                            \
        QActive *const ao_ = QF_ACTIVE_CAST((me_));                 \
        ((void)(*((QActiveVtable const *)(ao_->super.vptr))->post)( \
            ao_, QF_NO_MARGIN, (enum_t)(sig_), (QParam)(par_)));    \
    } while (false)

/*! Polymorphically posts an event to an active object (FIFO)
 * without delivery guarantee (task context).
 */
/**
 * @description
 * This macro does not assert if the queue overflows and cannot accept
 * the event with the specified margin of free slots remaining.
 *
 * @param[in,out] me_     pointer (see @ref oop)
 * @param[in]     margin_ the minimum free slots in the queue, which
 *                must still be available after posting the event.
 *                The special value #QF_NO_MARGIN causes asserting failure
 *                in case event allocation fails.
 * @param[in]     sig_    signal of the event to post
 * @param[in]     par_    parameter of the event to post.
 *
 * @returns
 * 'true' if the posting succeeded, and 'false' if the posting failed
 * due to insufficient margin of free slots available in the queue.
 *
 * @usage
 * @include qfn_postx.c
 */
#define QACTIVE_POST_X(me_, margin_, sig_, par_)                                                 \
    do {                                                                                         \
        QActive *const ao_ = QF_ACTIVE_CAST((me_));                                              \
        ((*((QActiveVtable const *)((ao_)->super.vptr))->post)((ao_), (margin_), (enum_t)(sig_), \
                                                               (QParam)(par_)));                 \
    } while (false)

/*! Polymorphically posts an event to an active object (FIFO)
 * with delivery guarantee (ISR context).
 */
/**
 * @description
 * This macro asserts if the queue overflows and cannot accept the event.
 *
 * @param[in,out] me_   pointer (see @ref oop)
 * @param[in]     sig_  signal of the event to post
 * @param[in]     par_  parameter of the event to post.
 *
 * @sa QACTIVE_POST_X(), QActive_postX_().
 *
 * @usage
 * @include qfn_post.c
 */
#define QACTIVE_POST_ISR(me_, sig_, par_)                              \
    do {                                                               \
        QActive *const ao_ = QF_ACTIVE_CAST((me_));                    \
        ((void)(*((QActiveVtable const *)(ao_->super.vptr))->postISR)( \
            ao_, QF_NO_MARGIN, (enum_t)(sig_), (QParam)(par_)));       \
    } while (false)

/*! Polymorphically posts an event to an active object (FIFO)
 * without delivery guarantee (ISR context).
 */
/**
 * @description
 * This macro does not assert if the queue overflows and cannot accept
 * the event with the specified margin of free slots remaining.
 *
 * @param[in,out] me_     pointer (see @ref oop)
 * @param[in]     margin_ the minimum free slots in the queue, which
 *                must still be available after posting the event.
 *                The special value #QF_NO_MARGIN causes asserting failure
 *                in case event allocation fails.
 * @param[in]     sig_    signal of the event to post
 * @param[in]     par_    parameter of the event to post.
 *
 * @returns
 * 'true' if the posting succeeded, and 'false' if the posting failed
 * due to insufficient margin of free slots available in the queue.
 *
 * @usage
 * @include qfn_postx.c
 */
#define QACTIVE_POST_X_ISR(me_, margin_, sig_, par_)                           \
    ((*((QActiveVtable const *)(QF_ACTIVE_CAST((me_))->super.vptr))->postISR)( \
        QF_ACTIVE_CAST((me_)), (margin_), (enum_t)(sig_), (QParam)(par_)))

/*! Implementation of the task-level event posting
 * @private @memberof QActive
 */
bool QActive_postX_(QActive *const me, uint_fast8_t margin, enum_t const sig, QParam const par);

/*! Implementation of the ISR-level event posting
 * @private @memberof QActive
 */
bool QActive_postXISR_(QActive *const me, uint_fast8_t margin, enum_t const sig, QParam const par);

#else /* no event parameter */

#define QACTIVE_POST(me_, sig_)                                                       \
    do {                                                                              \
        QActive *const ao_ = QF_ACTIVE_CAST((me_));                                   \
        ((void)(*((QActiveVtable const *)(ao_->super.vptr))->post)(ao_, QF_NO_MARGIN, \
                                                                   (enum_t)(sig_)));  \
    } while (false)

#define QACTIVE_POST_X(me_, margin_, sig_) \
    ((*((QActiveVtable const *)((me_)->super.vptr))->post)((me_), (margin_), (sig_)))

bool QActive_postX_(QActive *const me, uint_fast8_t margin, enum_t const sig);

#define QACTIVE_POST_ISR(me_, sig_)                                                      \
    do {                                                                                 \
        QActive *const ao_ = QF_ACTIVE_CAST((me_));                                      \
        ((void)(*((QActiveVtable const *)(ao_->super.vptr))->postISR)(ao_, QF_NO_MARGIN, \
                                                                      (enum_t)(sig_)));  \
    } while (false)

#define QACTIVE_POST_X_ISR(me_, margin_, sig_)                                 \
    ((*((QActiveVtable const *)(QF_ACTIVE_CAST((me_))->super.vptr))->postISR)( \
        QF_ACTIVE_CAST((me_)), (margin_), (enum_t)(sig_)))

bool QActive_postXISR_(QActive *const me, uint_fast8_t margin, enum_t const sig);
#endif

#if (QF_TIMEEVT_CTR_SIZE != 0U)

/*! Processes all armed time events at every clock tick. */
void QF_tickXISR(uint_fast8_t const tickRate);

#ifdef QF_TIMEEVT_PERIODIC
/*! Arm the QP-nano one-shot time event.
 * @public @memberof QActive
 */
void QActive_armX(QActive *const me, uint_fast8_t const tickRate, QTimeEvtCtr const nTicks,
                  QTimeEvtCtr const interval);
#else
/*! Arm the QP-nano one-shot time event.
 * @public @memberof QActive
 */
void QActive_armX(QActive *const me, uint_fast8_t const tickRate, QTimeEvtCtr const nTicks);
#endif

/*! Disarm a time event. Since the tick counter
 * @public @memberof QActive
 */
void QActive_disarmX(QActive *const me, uint_fast8_t const tickRate);

#ifdef QF_TIMEEVT_USAGE
/*! timer is running
 * @public @memberof QActive
 */
bool QActive_armedX(QActive *const me, uint_fast8_t const tickRate);

#endif /* QF_TIMEEVT_USAGE */

#endif /* (QF_TIMEEVT_CTR_SIZE != 0U) */

/****************************************************************************/
/* QF-nano protected methods ...*/

/*! QF-nano initialization. */
void QF_init(uint_fast8_t maxActive);

/*! QF-nano termination. */
/**
 * @description
 * This function terminates QF and performs any necessary cleanup.
 * In QF-nano this function is defined in the BSP. Many QF ports might not
 * require implementing QF_stop() at all, because many embedded applications
 * don't have anything to exit to.
 */
void QF_stop(void);

/*! Startup QF-nano callback. */
/**
 * @description
 * The time line for calling QF_onStartup() depends on the particular
 * QF port. In most cases, QF_onStartup() is called from QF_run(), right
 * before starting any multitasking kernel or the background loop.
 *
 * @sa QF initialization example for ::QActiveCB.
 */
void QF_onStartup(void);

/*! Transfers control to QF-nano to run the application. */
int_t QF_run(void);

/****************************************************************************/
/*! QActive Control Block
 *
 * QActiveCB represents the read-only information that the QF-nano needs to
 * manage the active object. QActiveCB objects are grouped in the array
 * QF_active[], which typically can be placed in ROM.
 *
 * @usage
 * The following example illustrates how to allocate and initialize the
 * ::QActive control blocks in the array QF_active[].
 * @include qfn_main.c
 */
typedef struct {
    QActive *act; /*!< pointer to the active object structure */
    QEvt *queue;  /*!< pointer to the event queue buffer */
    uint8_t qlen; /*!< the length of the queue ring buffer */
} QActiveCB;

/** active object control blocks */
/*lint -save -e9067 MISRA-C:2012 Rule 8.11, extern array declared without size
 */
extern QActiveCB const Q_ROM QF_active[];
/*lint -restore */

/*! number of active objects in the application (# elements in QF_active[]) */
extern uint_fast8_t QF_maxActive_;

/*! Ready set of QF-nano. */
extern uint_fast8_t volatile QF_readySet_;

#ifndef QF_LOG2

/*! Lookup table for (log2(n) + 1), where n is the index into the table.
 * This lookup delivers the 1-based number of the most significant 1-bit
 * of a nibble.
 */
extern uint8_t const Q_ROM QF_log2Lkup[16];

#endif

#ifdef QF_TIMEEVT_USAGE

/*! Timer set of QF-nano. */
extern uint_fast8_t volatile QF_timerSetX_[QF_MAX_TICK_RATE];

#endif /* QF_TIMEEVT_USAGE */

/*! Lookup table for ~(1 << (n - 1)), where n is the index into the table. */
extern uint8_t const Q_ROM QF_invPow2Lkup[9];

/****************************************************************************/
/*! This macro encapsulates accessing the active object queue at a
 * given index, which violates MISRA-C 2004 rules 17.4(req) and 11.4(adv).
 * This macro helps to localize this deviation.
 */
#define QF_ROM_QUEUE_AT_(ao_, i_) (((QEvt *)Q_ROM_PTR((ao_)->queue))[(i_)])

/*! This macro encapsulates accessing the active object control block,
 * which violates MISRA-C 2004 rule 11.4(adv). This macro helps to localize
 * this deviation.
 */
#define QF_ROM_ACTIVE_GET_(p_) ((QActive *)Q_ROM_PTR(QF_active[(p_)].act))

/*! This macro encapsulates the upcast to QActive*
 *
 * This macro encapsulates up-casting a pointer to a subclass of ::QActive
 * to the base class ::QActive, which violates MISRA-C 2004 rule 11.4(adv).
 * This macro helps to localize this deviation.
 */
#define QF_ACTIVE_CAST(a_) ((QActive *)(a_))

#endif /* QFN_H */
